home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / scope / 051-075 / scopedisk64 / blk / src / lex.c < prev    next >
C/C++ Source or Header  |  1995-03-19  |  26KB  |  897 lines

  1. /*
  2.  * Lexical Analysis Functions Takes input stream and returns "tokens".
  3.  * Implements pre-processor, allowing #include's and #define's tokens are
  4.  * character strings.
  5.  */
  6. #include <stdio.h>
  7. #include <ctype.h>
  8. #include <functions.h>
  9. #include <exec/types.h>
  10. #include "listmac.h"
  11. #include "std.h"
  12. #include "lex.h"
  13.  
  14.  
  15. struct StringElt {
  16.         struct Nod      node;
  17.         char           *buf;
  18. };
  19.  
  20. struct Token {
  21.         struct Nod      node;
  22.         char           *buf;
  23.         short           type, can_be_macro;
  24.         char            chr;
  25. };
  26.  
  27. struct Macro {
  28.         struct Nod      node;
  29.         struct Lst      arguments;
  30.         struct Lst      tokens;
  31.         char           *name;
  32.         short           active, nargs;
  33. };
  34.  
  35. struct ActiveMacro {
  36.         struct Nod      node;
  37.         struct Macro   *mac;
  38.         struct Token   *curtok;
  39. };
  40.  
  41. struct LexFile {
  42.         struct Nod      node;
  43.         FILE           *fp;
  44.         short           newln;
  45.         short           nextchar;
  46. };
  47.  
  48. struct IfBlock {
  49.         struct Nod     *node;
  50.         short           skip_else;
  51. };
  52.  
  53.  
  54. struct Token   *CopyToken ();
  55. struct Macro   *FindMacro ();
  56. char           *FindString ();
  57.  
  58.  
  59. #define BUFLEN 100
  60. static char     buf[BUFLEN];
  61. static short    bufpos;
  62. static struct Token
  63.                 rawtok, *lasttok, *retok;
  64.  
  65. static struct LexFile *toplf;
  66. static struct Lst macros, activemacs, files, ifblocks;
  67. static short    files_open = 0, init_done = 0;
  68. static char     *includestr, *definestr, *ifdefstr,
  69.                 *ifndefstr, *elsestr, *endifstr;
  70.  
  71. static char    *libbase = "hd:include/", libnam[80], basnam[80];
  72.  
  73. #define NHASH 53
  74. struct Lst      hashTab[NHASH];
  75.  
  76.  
  77. /*
  78.  * Open a file for lexing - could be an include.
  79.  * Set newline flag to true.
  80.  */
  81. short OpenLexFile (name)
  82.         char           *name;
  83. {
  84.         struct LexFile *lf;
  85.         struct Macro   *mac;
  86.  
  87.         /*
  88.          * on first call, init environment vars 
  89.          */
  90.         if (!init_done) {
  91.                 NewList (&files);
  92.                 NewList (¯os);
  93.                 NewList (&ifblocks);
  94.                 NewList (&activemacs);
  95.                 InitHash ();
  96.                 includestr = FindString ("include");
  97.                 definestr = FindString ("define");
  98.                 ifdefstr = FindString ("ifdef");
  99.                 ifndefstr = FindString ("ifndef");
  100.                 elsestr = FindString ("else");
  101.                 endifstr = FindString ("endif");
  102.                 init_done = 1;
  103.         }
  104.  
  105.         /*
  106.          * if there are no files currently open, prepare for a new one 
  107.          */
  108.         if (!files_open) {
  109.                 while (mac = (struct Macro *) RemHead (¯os))
  110.                         FreeMacro (mac);
  111.                 ASSERT (!TEST (HEAD (&ifblocks)));
  112.                 ASSERT (!TEST (HEAD (&activemacs)));
  113.                 retok = NULL;
  114.         }
  115.         lf = NEW (struct LexFile);
  116.         if (!lf) return 0;
  117.  
  118.         /*
  119.          * printf ("opening \"%s\"\n", name); 
  120.          */
  121.         lf->fp = fopen (name, "r");
  122.         if (!lf->fp) {
  123.                 FREI (lf);
  124.                 return 0;
  125.         }
  126.         lf->newln = 1;
  127.         lf->nextchar = getc (lf->fp);
  128.         AddHead (&files, lf);
  129.         toplf = lf;
  130.  
  131.         if (feof (lf->fp)) {
  132.                 CloseLexFile ();
  133.                 return 0;
  134.         }
  135.         files_open = 1;
  136.         return 1;
  137. }
  138.  
  139.  
  140. /*
  141.  * Close top file returning 1 for the last file now closed.
  142.  */
  143. short CloseLexFile ()
  144. {
  145.         if (!files_open) return 1;
  146.  
  147.         fclose (toplf->fp);
  148.         Remove (toplf);
  149.         FREI (toplf);
  150.         toplf = HEAD (&files);
  151.         if (TEST (toplf)) return 0;
  152.         files_open = 0;
  153.         toplf = 0;
  154.         return 1;
  155. }
  156.  
  157.  
  158. /*
  159.  * free stuff up 
  160.  */
  161. LexCleanup ()
  162. {
  163.         struct Macro   *mac, *nmac;
  164.  
  165.         while (files_open) CloseLexFile ();
  166.  
  167.         FreeHash ();
  168.         for (mac = HEAD (¯os); nmac = NEXT (mac); mac = nmac)
  169.                 FreeMacro (mac);
  170.         ASSERT (!TEST (HEAD (&activemacs)));
  171.  
  172.         /*
  173.          * initialization can be done again 
  174.          */
  175.         init_done = 0;
  176. }
  177.  
  178.  
  179. FreeMacro (mac)
  180.         struct Macro   *mac;
  181. {
  182.         struct Token   *tok;
  183.         struct StringElt *se;
  184.  
  185.         while (tok = (struct Token *) RemHead (&mac->tokens))
  186.                 FREI (tok);
  187.         while (se = (struct StringElt *) RemHead (&mac->arguments))
  188.                 FREI (se);
  189.         FREI (mac);
  190. }
  191.  
  192.  
  193. /*
  194.  * Get next raw character from file dealing with backslash escape chars.
  195.  * This is kind of wrong since these really ought to be recognized only
  196.  * inside strings.  Doing it this way deals with backslashes before
  197.  * newlines.
  198.  */
  199. short NextC1 (fp)
  200.         FILE           *fp;
  201. {
  202.         short           c, k, radix;
  203.  
  204.         c = getc (fp);
  205.         if (c == '\\') {
  206.                 c = getc (fp);
  207.                 if (isdigit (c)) {
  208.                         radix = 10;
  209.                         if (c == '0') radix = 8;
  210.                         k = (c - '0');
  211.                         while (isdigit (c = getc (fp)))
  212.                                 k = k * radix + (c - '0');
  213.                         ungetc (c, fp);
  214.                         c = k;
  215.                 } else {
  216.                         switch (c) {
  217.                             case '\\':
  218.                                 break;
  219.                             case '"':
  220.                                 c = '.';
  221.                                 break;
  222.                             case 'n':
  223.                                 c = '\n';
  224.                                 break;
  225.                             case 'b':
  226.                                 c = '\b';
  227.                                 break;
  228.                             case 't':
  229.                                 c = '\t';
  230.                                 break;
  231.                             case '\n':
  232.                                 c = getc (fp);
  233.                         }
  234.                 }
  235.         }
  236.         return c;
  237. }
  238.  
  239.  
  240. /*
  241.  * Get next character from Top Lex File, updating nextchar.
  242.  */
  243. short NextC ()
  244. {
  245.         short           c;
  246.  
  247.         if (!toplf) return EOF;
  248.         c = toplf->nextchar;
  249.         toplf->nextchar = NextC1 (toplf->fp);
  250.         return c;
  251. }
  252.  
  253.  
  254. /*
  255.  * Move the top lex file to the next newline.
  256.  */
  257. AdvanceEOL ()
  258. {
  259.         if (!toplf) return;
  260.         while (toplf->nextchar != '\n' && !feof (toplf->fp))
  261.                 NextC ();
  262. }
  263.  
  264.  
  265. /*
  266.  * Read a raw set of characters updating newline state.  Sets the
  267.  * fields in rawtok to the type and string pointer, if any. 
  268.  */
  269. RawToken ()
  270. {
  271.         short           c;
  272.         short           nochar = 1, comment, nl;
  273.  
  274.         bufpos = 0;
  275.         rawtok.can_be_macro = 1;
  276.  
  277.         /*
  278.          * get a non-blank char 
  279.          */
  280.         while (nochar) {
  281.  
  282.                 /*
  283.                  * get a character testing for end-of-file if EOF, just
  284.                  * return that fact right away 
  285.                  */
  286.                 c = NextC ();
  287.                 if (c == EOF) {
  288.                         rawtok.type = RT_EOF;
  289.                         return;
  290.                 }
  291.  
  292.                 /*
  293.                  * test next char: if it's white space other than newline,
  294.                  * just clear the newline flag and continue looking. 
  295.                  */
  296.                 if (isspace (c) && c != '\n')
  297.                         toplf->newln = 0;
  298.                 else {
  299.  
  300.                         /*
  301.                          * otherwise, this is potentially interesting, but it
  302.                          * might be the start of a comment - if so, scan til
  303.                          * the end of the comment (no nested comments ala
  304.                          * "C").  If not, exit the loop. 
  305.                          */
  306.                         if (c == '/' && toplf->nextchar == '*') {
  307.                                 comment = 1;
  308.                                 while (comment) {
  309.                                         c = NextC ();
  310.                                         if (c == '*'
  311.                                           && toplf->nextchar == '/') {
  312.                                                 c = NextC ();
  313.                                                 comment = 0;
  314.                                         }
  315.                                 }
  316.  
  317.                         } else nochar = 0;
  318.                 }
  319.         }
  320.  
  321.         nl = toplf->newln;
  322.         toplf->newln = 0;
  323.         bufpos = 0;
  324.  
  325.         /*
  326.          * otherwise, read out the cases id symbol starting with char 
  327.          */
  328.         if (nl && c == '#')
  329.                 rawtok.type = RT_ESC;
  330.  
  331.         else if (c == '\'') {
  332.                 rawtok.chr = NextC ();
  333.                 c = NextC ();
  334.  
  335.                 /*
  336.                  * Skip past weird character constants, like 'FOOL',
  337.                  * making no attempt to deal with them correctly.
  338.                  */
  339.                 comment = 0;
  340.                 while (c != '\'' && comment < 6) {
  341.                         c = NextC ();
  342.                         comment++;
  343.                 }
  344.                 if (comment >= 6) printf ("error in character constant\n");
  345.                 rawtok.type = RT_CHRC;
  346.  
  347.         } else if (isalpha (c) || c == '_') {
  348.                 buf[bufpos++] = c;
  349.                 while (isalnum (toplf->nextchar) || toplf->nextchar == '_')
  350.                         buf[bufpos++] = NextC ();
  351.                 buf[bufpos] = 0;
  352.                 rawtok.type = RT_ID;
  353.                 rawtok.buf = FindString (buf);
  354.  
  355.         } else if (c == '\n') {
  356.                 toplf->newln = 1;
  357.                 rawtok.type = RT_NEWLN;
  358.  
  359.         } else if (isdigit (c)) {
  360.                 buf[bufpos++] = c;
  361.                 while (isdigit (toplf->nextchar)) buf[bufpos++] = NextC ();
  362.                 buf[bufpos] = 0;
  363.                 rawtok.buf = FindString (buf);
  364.                 rawtok.type = RT_NUM;
  365.  
  366.         } else if (c == '"') {
  367.                 while ((c = NextC ()) != '"')
  368.                         if (bufpos < BUFLEN)
  369.                                 buf[bufpos++] = c;
  370.  
  371.                 if (bufpos + 1 == BUFLEN) printf ("string overflow\n");
  372.                 buf[bufpos] = 0;
  373.                 rawtok.buf = FindString (buf);
  374.                 rawtok.type = RT_STR;
  375.  
  376.         } else {
  377.                 rawtok.chr = c;
  378.                 rawtok.type = RT_CHR;
  379.         }
  380. }
  381.  
  382.  
  383. /*
  384.  * Main function -- returns string pointer for next token makes include files
  385.  * and macros transparent reads from top open file and returns pointer to
  386.  * string of chars could be a symbol, could be a number, could be a character 
  387.  */
  388. short NextToken (tbuf)
  389.         char          **tbuf;
  390. {
  391.         struct Token   *tok;
  392.         struct Macro   *mac;
  393.         struct ActiveMacro *amac;
  394.         short           i;
  395.         char            c;
  396.  
  397.         while () {
  398.  
  399.                 /*
  400.                  * get raw token from file or active macro 
  401.                  */
  402.                 amac = HEAD (&activemacs);
  403.                 if (retok) {
  404.                         tok = retok;
  405.                         retok = NULL;
  406.                 } else if (TEST (amac)) {
  407.                         tok = amac->curtok;
  408.                         if (!TEST (tok)) {
  409.                                 amac->mac->active = 0;
  410.                                 for (i = 0; i < amac->mac->nargs; i++)
  411.                                         FreeMacro (RemHead (¯os));
  412.                                 Remove (amac);
  413.                                 FREI (amac);
  414.                                 continue;
  415.                         }
  416.                         amac->curtok = NEXT (amac->curtok);
  417.                 } else {
  418.                         RawToken ();
  419.                         tok = &rawtok;
  420.                 }
  421.                 switch (tok->type) {
  422.                     case RT_EOF:
  423.                         if (CloseLexFile ()) {
  424.                                 lasttok = tok;
  425.                                 return RT_EOF;
  426.                         }
  427.                         break;
  428.                     case RT_ESC:
  429.                         RawToken ();
  430.                         if (rawtok.type != RT_ID) {
  431.                                 printf ("real problem with lexer directive\n");
  432.                                 AdvanceEOL ();
  433.                                 break;
  434.                         }
  435.                         if (rawtok.buf == includestr)           Include ();
  436.                         else if (rawtok.buf == definestr)       Define ();
  437.                         else if (rawtok.buf == ifdefstr)        IfDef (1);
  438.                         else if (rawtok.buf == ifndefstr)       IfDef (0);
  439.                         else if (rawtok.buf == elsestr)         Else ();
  440.                         else if (rawtok.buf == endifstr)        EndIf ();
  441.                         else {
  442.                                 printf ("unknown directive \"%s\"\n", rawtok.buf);
  443.                                 AdvanceEOL ();
  444.                         }
  445.                         break;
  446.  
  447.                     case RT_ID:
  448.  
  449.                         /*
  450.                          * test for known macros (if this token can be one) 
  451.                          */
  452.                         if (tok->can_be_macro) {
  453.                                 mac = FindMacro (tok->buf);
  454.                                 if (mac) {
  455.                                         InstantiateMacro (mac);
  456.                                         break;
  457.                                 }
  458.                         }
  459.                     case RT_STR:
  460.                     case RT_NUM:
  461.                         *tbuf = tok->buf;
  462.                         lasttok = tok;
  463.                         return tok->type;
  464.                     case RT_CHR:
  465.                     case RT_CHRC:
  466.                         *tbuf = &tok->chr;
  467.                         lasttok = tok;
  468.                         return tok->type;
  469.                 }
  470.         }
  471. }
  472.  
  473.  
  474. /*
  475.  * The given macro has occured in the text, cause it to come into existence
  476.  * with argument substitution. 
  477.  */
  478. InstantiateMacro (mac)
  479.         struct Macro   *mac;
  480. {
  481.         struct ActiveMacro *amac;
  482.         struct StringElt *arg;
  483.         struct Macro   *smac;
  484.         struct Token   *tok;
  485.         char           *buf;
  486.         short           vtok, level, endarglist;
  487.  
  488.         if (mac->active) {
  489.                 printf ("recursive macro ignored\n");
  490.                 return;
  491.         }
  492.  
  493.         /*
  494.          * read arguments, if any, and make them macros in their own right
  495.          * arguments are read recursively using NextToken (right? I'm not
  496.          * sure...) 
  497.          */
  498.         arg = HEAD (&mac->arguments);
  499.         if (TEST (arg)) {
  500.  
  501.                 /*
  502.                  * get first open paren 
  503.                  */
  504.                 vtok = NextToken (&buf);
  505.                 if (vtok != RT_CHR || *buf != '(')
  506.                         goto argfail;
  507.  
  508.                 /*
  509.                  * loop through args separated with commas 
  510.                  */
  511.                 endarglist = 0;
  512.                 while (TEST (arg)) {
  513.                         smac = NEW (struct Macro);
  514.                         if (!smac) goto argfail;
  515.                         smac->name = arg->buf;
  516.                         smac->active = 0;
  517.                         smac->nargs = 0;
  518.                         NewList (&smac->arguments);
  519.                         NewList (&smac->tokens);
  520.  
  521.                         /*
  522.                          * scan out tokens allowing for nested commas 
  523.                          */
  524.                         level = 0;
  525.                         while () {
  526.                                 vtok = NextToken (&buf);
  527.                                 if (vtok == RT_CHR) {
  528.                                         if (*buf == '(' || *buf == '{')
  529.                                                 level++;
  530.                                         if (*buf == '}')
  531.                                                 level--;
  532.                                         if (*buf == ')') {
  533.                                                 if (level == 0) {
  534.                                                         endarglist = 1;
  535.                                                         break;
  536.                                                 }
  537.                                                 level--;
  538.                                         }
  539.                                         if (*buf == ',' && level == 0)
  540.                                                 break;
  541.                                 }
  542.                                 tok = CopyToken (lasttok);
  543.                                 if (!tok) goto argfail;
  544.                                 tok->can_be_macro = 0;
  545.                                 AddTail (&smac->tokens, tok);
  546.                         }
  547.                         AddHead (¯os, smac);
  548.                         if (endarglist) break;
  549.                         arg = NEXT (arg);
  550.                 }
  551.                 if (vtok != RT_CHR || *buf != ')') goto argfail;
  552.         }
  553.  
  554.         /*
  555.          * Macro does not become active in the global list until all
  556.          * arguments have been parsed and interpreted. 
  557.          */
  558.         amac = NEW (struct ActiveMacro);
  559.         if (!amac) return;              /* what the fuck do I do here? */
  560.         amac->mac = mac;
  561.         amac->curtok = HEAD (&mac->tokens);
  562.         mac->active = 1;
  563.         AddHead (&activemacs, amac);
  564.         return;
  565.  
  566. argfail:
  567.         printf ("bad problem with arguments\n");
  568. }
  569.  
  570.  
  571. /*
  572.  * locate a specified macro in the available macros list 
  573.  */
  574. struct Macro * FindMacro (buf)
  575.         char           *buf;
  576. {
  577.         struct Macro   *mac;
  578.  
  579.         for (mac = HEAD (¯os); TEST (mac); mac = NEXT (mac))
  580.                 if (mac->name == buf)
  581.                         return mac;
  582.  
  583.         return NULL;
  584. }
  585.  
  586.  
  587. /*
  588.  * Do an "include" directive 
  589.  */
  590. Include ()
  591. {
  592.         short           pos, ok, global;
  593.         char            c;
  594.  
  595.         /*
  596.          * get filename 
  597.          */
  598.         RawToken ();
  599.         ok = 0;
  600.         global = 0;
  601.  
  602.         if (rawtok.type == RT_STR) {
  603.                 ok = 1;
  604.                 strcpy (basnam, rawtok.buf);
  605.         } else if (rawtok.type == RT_CHR && rawtok.chr == '<') {
  606.                 ok = 1;
  607.                 pos = 0;
  608.                 while () {
  609.                         c = NextC ();
  610.                         if (c == '>') break;
  611.                         basnam[pos++] = c;
  612.                 }
  613.                 basnam[pos] = 0;
  614.                 global = 1;
  615.         }
  616.         AdvanceEOL ();
  617.         if (!ok) {
  618.                 printf ("no file to include\n");
  619.                 return;
  620.         }
  621.         if (!global) if (OpenLexFile (basnam)) return;
  622.         strcpy (libnam, libbase);
  623.         strcat (libnam, basnam);
  624.         if (OpenLexFile (libnam)) return;
  625.         printf ("error open include file <%s>\n", libnam);
  626. }
  627.  
  628.  
  629. /*
  630.  * Do the "define" directive.
  631.  */
  632. Define ()
  633. {
  634.         struct Macro   *mac = NULL;
  635.         struct Token   *tok;
  636.         struct StringElt *se;
  637.  
  638.         /*
  639.          * get identifier to define 
  640.          */
  641.         RawToken ();
  642.         if (rawtok.type != RT_ID) {
  643.                 printf ("error in define - no identifier\n");
  644.                 goto escape;
  645.         }
  646.         mac = NEW (struct Macro);
  647.         if (!mac) goto escape;
  648.         mac->name = rawtok.buf;
  649.         mac->active = 0;
  650.         mac->nargs = 0;
  651.         NewList (&mac->arguments);
  652.         NewList (&mac->tokens);
  653.  
  654.         /*
  655.          * Look for parenthized argument list.
  656.          */
  657.         if (toplf->nextchar == '(') {
  658.                 RawToken ();
  659.                 while () {
  660.                         RawToken ();
  661.  
  662.                         /*
  663.                          * deal with special case of "#define MACRO()" 
  664.                          */
  665.                         if (!mac->nargs && rawtok.type == RT_CHR
  666.                           && rawtok.chr == ')')
  667.                                 break;
  668.                         if (rawtok.type != RT_ID) {
  669.                                 printf ("macro argument not an identifier\n");
  670.                                 goto escape;
  671.                         }
  672.                         se = NEW (struct StringElt);
  673.                         if (!se) goto escape;
  674.                         se->buf = rawtok.buf;
  675.                         AddTail (&mac->arguments, se);
  676.                         mac->nargs++;
  677.                         RawToken ();
  678.                         if (rawtok.type != RT_CHR) {
  679.                                 printf ("macro arg list delimiter not a character\n");
  680.                                 goto escape;
  681.                         }
  682.                         if (rawtok.chr == ')') break;
  683.                         if (rawtok.chr != ',') {
  684.                                 printf ("macro arg list delimiter not ',' or ')'\n");
  685.                                 goto escape;
  686.                         }
  687.                 }
  688.         }
  689.  
  690.         /*
  691.          * get the sequence of tokens which make up this definition 
  692.          */
  693.         while () {
  694.                 RawToken ();
  695.                 if (rawtok.type == RT_NEWLN || rawtok.type == RT_EOF) break;
  696.                 tok = CopyToken (&rawtok);
  697.                 if (!tok) goto escape;
  698.                 tok->can_be_macro = 1;
  699.                 AddTail (&mac->tokens, tok);
  700.         }
  701.  
  702.         AddHead (¯os, mac);
  703.         return;
  704.  
  705. escape:
  706.         if (mac) FreeMacro (mac);
  707.         AdvanceEOL ();
  708. }
  709.  
  710.  
  711. /*
  712.  * Skip over a defined-out section.  Returns 1 if it ends with #else, 0
  713.  * if it ends with #endif, -1 for EOF.  Keeps track of nested if's
  714.  * being skipped.
  715.  */
  716. short SkipRemovedSec ()
  717. {
  718.         short           level = 0;
  719.  
  720.         while () {
  721.                 RawToken ();
  722.                 if (rawtok.type == RT_EOF) return -1;
  723.                 if (rawtok.type == RT_ESC) {
  724.                         RawToken ();
  725.                         if (rawtok.type == RT_ID) {
  726.                                 if (rawtok.buf == ifdefstr
  727.                                   || rawtok.buf == ifndefstr) level++;
  728.                                 if (rawtok.buf == elsestr)
  729.                                         if (!level) return 1;
  730.                                 if (rawtok.buf == endifstr)
  731.                                         if (!level--) return 0;
  732.                         }
  733.                 }
  734.         }
  735. }
  736.  
  737.  
  738. /*
  739.  * Does the "ifdef"/"ifndef" - "else" - "endif" directive.  Type is 1
  740.  * for ifdef and 0 for ifndef.
  741.  */
  742. IfDef (type)
  743.         short           type;
  744. {
  745.         struct Macro   *mac;
  746.         struct IfBlock *ib;
  747.         short           els;
  748.  
  749.         RawToken ();
  750.         mac = FindMacro (rawtok.buf);
  751.  
  752.         /*
  753.          * If condition is true, add a node to say "skip the next #else
  754.          * section" if false, skip this section.  If section ends with an
  755.          * else, add a junk node to pop off for consistency at next #endif.
  756.          */
  757.         if ((mac && type) || (!mac && !type)) {
  758.                 ib = NEW (struct IfBlock);
  759.                 if (!ib) return;
  760.                 ib->skip_else = 1;
  761.                 AddHead (&ifblocks, ib);
  762.         } else {
  763.                 els = SkipRemovedSec ();
  764.                 if (els == 1) {
  765.                         ib = NEW (struct IfBlock);
  766.                         if (!ib) return;
  767.                         ib->skip_else = 0;
  768.                         AddHead (&ifblocks, ib);
  769.                 }
  770.         }
  771. }
  772.  
  773.  
  774. Else ()
  775. {
  776.         struct IfBlock *ib;
  777.         short           els;
  778.  
  779.         ib = HEAD (&ifblocks);
  780.         ASSERT (TEST (ib));
  781.         Remove (ib);
  782.         if (!ib->skip_else) {
  783.                 printf ("strange \"else\" block found\n");
  784.         } else {
  785.                 els = SkipRemovedSec ();
  786.                 if (els == 1 && ib->skip_else) {
  787.                         printf ("strange \"else\" block found\n");
  788.                 }
  789.         }
  790.         FREI (ib);
  791. }
  792.  
  793.  
  794. EndIf ()
  795. {
  796.         struct IfBlock *ib;
  797.  
  798.         ib = HEAD (&ifblocks);
  799.         ASSERT (TEST (ib));
  800.         Remove (ib);
  801.         FREI (ib);
  802. }
  803.  
  804.  
  805. /*
  806.  * Backs up one token 
  807.  */
  808. Backspace ()
  809. {
  810.         retok = lasttok;
  811. }
  812.  
  813.  
  814. struct Token * CopyToken (tok)
  815.         struct Token   *tok;
  816. {
  817.         struct Token   *ntok;
  818.  
  819.         ntok = NEW (struct Token);
  820.         if (ntok) *ntok = *tok;
  821.         return ntok;
  822. }
  823.  
  824.  
  825.  
  826. /* HASH TABLE STUFF */
  827.  
  828.  
  829. InitHash ()
  830. {
  831.         short           i;
  832.  
  833.         for (i = 0; i < NHASH; i++)
  834.                 NewList (&hashTab[i]);
  835. }
  836.  
  837.  
  838. /*
  839.  * Simple but (I hope) effective hash function.
  840.  */
  841. short HashVal (buf)
  842.         char           *buf;
  843. {
  844.         unsigned long   hash;
  845.  
  846.         hash = 0;
  847.         while (*buf) hash = hash * 3 + *buf++;
  848.         return hash % NHASH;
  849. }
  850.  
  851.  
  852. /*
  853.  * Finds (or creates) a stored string matching the given one return pointer
  854.  * which acts as unique ident for this string.
  855.  */
  856. char * FindString (buf)
  857.         char           *buf;
  858. {
  859.         short           hash;
  860.         struct Lst     *hlist;
  861.         struct StringElt *se;
  862.         char           *sto;
  863.  
  864.         hash = HashVal (buf);
  865.         hlist = &hashTab[hash];
  866.  
  867.         for (se = HEAD (hlist); TEST (se); se = NEXT (se)) {
  868.                 if (!strcmp (se->buf, buf))
  869.                         return se->buf;
  870.         }
  871.         se = NEW (struct StringElt);
  872.         if (!se) return NULL;
  873.         sto = NEW_N (char, strlen (buf) + 1);
  874.         if (!sto) {
  875.                 FREI (se);
  876.                 return NULL;
  877.         }
  878.         AddTail (hlist, se);
  879.         strcpy (sto, buf);
  880.         se->buf = sto;
  881.         return sto;
  882. }
  883.  
  884.  
  885. FreeHash ()
  886. {
  887.         short           i;
  888.         struct StringElt *se, *nse;
  889.  
  890.         for (i = 0; i < NHASH; i++) {
  891.                 for (se = HEAD (&hashTab[i]); nse = NEXT (se); se = nse) {
  892.                         FREE_N (se->buf, char, strlen (se->buf) + 1);
  893.                         FREI (se);
  894.                 }
  895.         }
  896. }
  897.